#include "portmonitor.h"
#include <QtDebug>
#include <QMutexLocker>

#ifdef Q_OS_WIN
#include "portmonitorwindows.h"
#else
#include "portmonitorlinux.h"
#endif

OnMonitorListener::~OnMonitorListener() {
}

PortFilterInfo::PortFilterInfo(int pid, int vid) :
    pid(pid),
    vid(vid)
{
}

bool PortFilterInfo::match(int pid, int vid) {
    return ((this->pid <= 0 || (this->pid == pid))
         && (this->vid <= 0 || (this->vid == vid)));
}

bool PortFilterInfo::match(QSerialPortInfo& info) {
    return ((pid <= 0 || (info.hasProductIdentifier() && pid == info.productIdentifier()))
         && (vid <= 0 || (info.hasVendorIdentifier() && vid == info.vendorIdentifier())));
}

PortMonitor::PortMonitor(QList<PortFilterInfo>& filters, QObject *parent) :
    QObject(parent),
    mFilters(filters),
    mLock(QMutex::Recursive)
{
}

PortMonitor::~PortMonitor() {
}

PortMonitor* PortMonitor::create(QObject* parent) {
    return create(QList<PortFilterInfo>(), parent);
}

PortMonitor* PortMonitor::create(int pid, int vid, QObject* parent) {
    QList<PortFilterInfo> filters;
    filters << PortFilterInfo(pid, vid);
    return create(filters, parent);
}

PortMonitor* PortMonitor::create(PortFilterInfo filter, QObject* parent) {
    QList<PortFilterInfo> filters;
    filters << filter;
    return create(filters, parent);
}

PortMonitor* PortMonitor::create(QList<PortFilterInfo> filters, QObject* parent) {
#ifdef Q_OS_WIN
    return new PortMonitorWindows(filters, parent);
#else
    return new PortMonitorLinux(filters, parent);
#endif
}

void PortMonitor::startMonitor(void* win, OnMonitorListener* pListener) {
    mpListener = pListener;

    onStartMonitor(win);
}

void PortMonitor::stopMonitor() {
    onStopMonitor();
}

void PortMonitor::loadPorts() {
    QMutexLocker locker(&mLock);

    mPortList.clear();

    for (QSerialPortInfo info : QSerialPortInfo::availablePorts()) {
        if (isMonitorPort(info)) {
            mPortList.append(info);
            qDebug() << "[" << info.portName() << "] pid: " << info.productIdentifier() << ", vid: " << info.vendorIdentifier();
        }
    }
}

QList<QSerialPortInfo> PortMonitor::getPorts() {
    QMutexLocker locker(&mLock);

    return mPortList;
}

QSerialPortInfo PortMonitor::getPort(QString name) {
    QMutexLocker locker(&mLock);

    for (QSerialPortInfo info : mPortList) {
        if (info.portName() == name) {
            return info;
        }
    }

    return QSerialPortInfo();
}

bool PortMonitor::isMonitorPort(QSerialPortInfo& info) {
    if (mFilters.isEmpty()) {
        return true; // treate as monitor port when no filter set
    }

    for (PortFilterInfo filter : mFilters) {
        if (filter.match(info)) {
            return true;
        }
    }
    return false;
}

void PortMonitor::notifyPortArrival(QString name) {
    if (mpListener != nullptr) {
        loadPorts();
        QSerialPortInfo info = getPort(name);
        qDebug() << "port arrival: " << name << "PID: " << info.productIdentifier() << ", VID: " << info.vendorIdentifier() ;
        if (isMonitorPort(info)) {
            qDebug() << "notifyPortArrival";
            mpListener->onPortArrival(name, info);
        }
    }
}

void PortMonitor::notifyPortRemoved(QString name) {
    if (mpListener != nullptr) {
        QSerialPortInfo info = getPort(name);
        qDebug() << "port removed: " << name << "PID: " << info.productIdentifier() << ", VID: " << info.vendorIdentifier();
        loadPorts();
        if (isMonitorPort(info)) {
            qDebug() << "notifyPortRemoved";
            mpListener->onPortRemoved(name, info);
        }
    }
}
